#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#ifdef __APPLE__
#import <mach/mach_host.h>
#import <mach/host_info.h>
#endif
#ifdef __linux__
#include <sys/sysinfo.h>
#include <linux/version.h>
#endif

#include "misc.h"

size_t physmem_pagesize() {

#if defined _SC_PAGESIZE || defined _SC_PAGE_SIZE
# ifdef _SC_PAGESIZE
    long pagesize = sysconf(_SC_PAGESIZE);
# else
    long pagesize = sysconf(_SC_PAGE_SIZE);
# endif
    if (pagesize >= 0)
        return pagesize;
#elif defined CTL_HW && defined HW_PAGESIZE
    int pagesize;
    size_t len = sizeof(pagesize);
    int mib[2] = { CTL_HW, HW_PAGESIZE };
    if (sysctl(mib, ARRAY_SIZE(mib), &physmem, &len, NULL, 0) == 0)
        return pagesize;
#endif
    return 0;
}

size_t physmem_total() {

#if defined _SC_PHYS_PAGES
    long pagenum = sysconf(_SC_PHYS_PAGES);
    if (pagenum >= 0)
        return pagenum * physmem_pagesize();
#elif defined CTL_HW && (defined HW_MEMSIZE || defined HW_PHYSMEM64)
    size_t physmem;
    size_t len = sizeof(physmem);
# ifdef HW_MEMSIZE
    /* OSX */
    int mib[2] = { CTL_HW, HW_MEMSIZE };
# else
    /* BSD */
    int mib[2] = { CTL_HW, HW_PHYSMEM64 };
# endif
    if (sysctl(mib, ARRAY_SIZE(mib), &physmem, &len, NULL, 0) == 0)
        return physmem;
#endif

#if defined __linux__
#  if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 16)
    struct sysinfo info;
    if (sysinfo(&info) == 0)
        return info.totalram;
#  endif
#endif

    return 0;
}

size_t physmem_available() {

#ifdef _SC_AVPHYS_PAGES
   /* linux  */
    long pagenum = sysconf(_SC_AVPHYS_PAGES);
    if (pagenum >= 0)
        return pagenum * physmem_pagesize();
#elif defined __APPLE__
    /* OSX */
    vm_statistics_data_t vmstat;
    mach_msg_type_number_t count = HOST_VM_INFO_COUNT;
    if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vmstat, &count) == KERN_SUCCESS)
        return vmstat.free_count * physmem_pagesize();
#elif defined BSD
    /* BSD */
    unsigned int free;
    size_t size = sizeof(free);
    if (sysctlbyname("vm.stats.vm.v_free_count", &free, &size, NULL, 0) == 0)
        return free * physmem_page_size();
#endif

#if defined __linux__
#  if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 16)
    struct sysinfo info;
    if (sysinfo(&info) == 0)
        return info.freeram;
#  endif
#endif

    return 0;
}
